#include <cstdlib>
#include <ctime>

#include <Geex/graphics/geexapp.h>
#include <Geex/basics/file_system.h>
#include <glut_viewer/tweak_bar.h>
#include <glut_viewer/glut_viewer.h>
#include <GL/GLU.h>
#include <Geex/third_party/misc/gl2ps.h>

#include <CGAL/Timer.h>

#include "packing_aggregator.h"
#include "../common/error_info.h"


namespace Polygon_particle {
		
	using namespace Geex;
	using namespace Packing_common;

	void TW_CALL tw_optimize(void*);
	void TW_CALL tw_perturb(void*);
	void TW_CALL tw_shuffle(void*);
	void TW_CALL tw_pack(void*);
	void TW_CALL tw_print_pdf(void*);
	void TW_CALL tw_trace(void*);
	void TW_CALL tw_print_polygons(void*);

    class Particle_packing_application : public GeexApp 
	{
    public:
        Particle_packing_application(int argc, char** argv) : GeexApp(argc, argv) 
		{ 
            hdr_ = false ;
	
			if (argc < 2)
				throw Bad_input("No circle file input.");

			polygon_filename = get_file_arg("txt");
			if (polygon_filename.empty())
				polygon_filename = get_file_arg("poly");

			debug_filename = get_file_arg("log");

			nb_trials = 10;

			polygon_filename = argv[1];
			std::cout<<"Used "<<polygon_filename<<" as input.\n";

		}

        Packing_aggregator* particle_packing_context() { return static_cast<Packing_aggregator*>(scene()) ; }

        void init_scene() 
		{
			scene_ = new Packing_aggregator(polygon_filename, debug_filename, 0.7);
        }

		void optimize()
		{
			particle_packing_context()->energy_minimize();
			glut_viewer_redraw();
		}
		void perturb()
		{
			particle_packing_context()->perturb();
			glut_viewer_redraw();
		}
		void pack()
		{
			particle_packing_context()->pack(nb_trials);
			glut_viewer_redraw();
		}
		void shuffle()
		{
			particle_packing_context()->shuffle();
			glut_viewer_redraw();
		}

		/////////////////// debug /////////////////////
		void trace()
		{
			particle_packing_context()->trace();
			glut_viewer_redraw();
		}

		GLboolean mouse(float x, float y, int button, enum GlutViewerEvent event) 
		{
			GLdouble pos[3];
			GLboolean hb;
			glut_viewer_get_picked_point(pos, &hb);
			if (event == GLUT_VIEWER_DOWN)
				particle_packing_context()->hand_select(pos[0], pos[1]);
			else if (event == GLUT_VIEWER_UP)
			{
				//std::cout<<pos[0]<<','<<pos[1]<<std::endl;
				particle_packing_context()->hand_move(pos[0], pos[1]);
				glut_viewer_redraw();
			}
			return GL_TRUE;
		}
		void print_polygons()
		{
			particle_packing_context()->print_polygons();
			std::cout<<"End printing.\n";
		}
		/////////////////////////////////////////////////
        void init_gui() 
		{
            GeexApp::init_gui() ;
				
			TwBar* function_bar = TwNewBar("Functions");
			TwDefine("Functions position='16 10' size='200 150' alpha=200");
			if (debug_filename.empty())
			{
				TwAddButton(function_bar, "Optimize", tw_optimize, NULL, "key=k");
				TwAddButton(function_bar, "Perturb", tw_perturb, NULL, "key=p");
				TwAddButton(function_bar, "Shuffle", tw_shuffle, NULL, "key=s");
				TwAddVarRW(function_bar, "Times", TW_TYPE_UINT32, &nb_trials, "min=1");
				TwAddButton(function_bar, "Pack", tw_pack, NULL, "key=P");
				TwAddButton(function_bar, "PDF", tw_print_pdf, NULL, "key=f");
				TwAddButton(function_bar, "Print Polygons", tw_print_polygons, NULL, "");
			}
			else
			{
				TwAddButton(function_bar, "Trace", tw_trace, NULL, "key=t");
			}
            TwBar* graphics_bar = TwNewBar("Graphics");
			TwDefine("Graphics position='16 160' size='200 50' alpha=200"); 
			TwAddVarRW(graphics_bar, "Polygons", TW_TYPE_BOOL8, &particle_packing_context()->toggle_polygons(), "");
			TwAddVarRW(graphics_bar, "Circles", TW_TYPE_BOOL8, &particle_packing_context()->toggle_circles(), "");

			*glut_viewer_is_enabled_ptr(GLUT_VIEWER_BACKGROUND) = GL_FALSE;
			viewer_properties_->visible() = GL_FALSE;
        }

    private:
		std::string polygon_filename;
		std::string debug_filename;
		unsigned int nb_trials;
    } ;


Polygon_particle::Particle_packing_application* particle_packing_app() { return static_cast<Polygon_particle::Particle_packing_application*>(Geex::GeexApp::instance()) ; }

void TW_CALL tw_optimize(void *clientData) { particle_packing_app()->optimize(); }
void TW_CALL tw_perturb(void *clientData)	{ particle_packing_app()->perturb(); }
void TW_CALL tw_pack(void *clientData)	{ particle_packing_app()->pack(); }
void TW_CALL tw_shuffle(void *clientData) { particle_packing_app()->shuffle(); }
void TW_CALL tw_trace(void *clientData)	{ particle_packing_app()->trace(); }
void TW_CALL tw_print_polygons(void *clientData)	{ particle_packing_app()->print_polygons(); }

void TW_CALL tw_print_pdf(void *clientData)
{
	std::cout << "Print to file MyFile.pdf ...";
	FILE *fp = fopen("MyFile.pdf", "wb");
	GLint buffsize = 0, state = GL2PS_OVERFLOW;
	GLint viewport[4];

	glGetIntegerv(GL_VIEWPORT, viewport);

	while( state == GL2PS_OVERFLOW ){ 
		buffsize += 1024*1024;//GL2PS_BSP_SORT 
		gl2psBeginPage ( "MyTitle", "MySoftware", viewport,
			GL2PS_PDF, GL2PS_BSP_SORT, GL2PS_SILENT |
			GL2PS_SIMPLE_LINE_OFFSET | GL2PS_NO_BLENDING |
			GL2PS_OCCLUSION_CULL | GL2PS_BEST_ROOT | 
			GL2PS_NO_PS3_SHADING | GL2PS_COMPRESS,
			GL_RGBA, 0, NULL, 2, 2, 2, buffsize,
			fp, "MyFile" );
		glut_viewer_redraw(); 
		state = gl2psEndPage();
	}

	fclose(fp);
	std::cout <<" End!" <<std::endl;
}
}

int main(int argc, char** argv) 
{
	//try
	//{
		Geex::initialize();
		Polygon_particle::Particle_packing_application app(argc, argv) ;
		app.main_loop() ;
		Geex::terminate() ;
	//}
//	catch(std::exception& e)
//	{
//		std::cerr<<"Exception thrown : "<<e.what()<<std::endl;
//#ifdef _DEBUG
//		system("pause");
//#endif
//	}
//	catch(...)
//	{
//		std::cerr<<"Unknown exception thrown.\n";
//#ifdef _DEBUG
//		system("pause");
//#endif
//	}
	return 0;
}
